﻿using System;
using System.Collections;
using System.Collections.Generic;
using linq = Microsoft.Linq.Expressions;
using System.Linq;
using Bling.Util;

namespace Bling.DSL {
  public abstract partial class Expr<T> {
    internal static readonly Operator<bool, T, T, T> ConditionOp = new Operator<bool, T, T, T>(Markers.ConditionMarker) {
      Apply = (a, b, c) => a ? b : c,
    };
    public Expr<S> Map<S>(Func<T, S> F, Func<S, T> G) {
      var op = new Operator<T, S>(new OperatorMarker("Coerce-" + typeof(S).Name) {
        Unary = e => {
          throw new Exception("Limitation: cannot use coerce yet in physical constraint.");
        },
      }) {
        Apply = t => F(t),
        Inverse = G == null ? ((Func<Expr<S>, Expr<T>>)null) : (s) => s.Map<T>(G, F),
        Linq = e => linq.Expression.Invoke(linq.Expression.Constant(F), e),
        Shader = e => e,

        DoConstantFold = false,
      };
      return op.Make(this);
    }
    public Expr<U> Combine<S, U>(Expr<S> v, Func<T, S, U> F) {
      var op = new Operator<T, S, U>(new OperatorMarker("Combine-" + typeof(S).Name + "-" + typeof(U).Name)) {
        Apply = (t, s) => F(t, s),
        Linq = (e1, e2) => linq.Expression.Invoke(linq.Expression.Constant(F), e1, e2),
        DoConstantFold = false,
      };
      return op.Make(this, v);
    }
    public Expr<V> Combine<S, U, V>(Expr<S> v1, Expr<U> v2, Func<T, S, U, V> F) {
      var op = new Operator<T, S, U, V>(new OperatorMarker("Combine-" + typeof(S).Name + "-" + typeof(U).Name + "-" + typeof(V).Name)) {
        Apply = (t, s, u) => F(t, s, u),
        Linq = (e1, e2, e3) => linq.Expression.Invoke(linq.Expression.Constant(F), e1, e2, e3),
      };
      return op.Make(this, v1, v2);
    }
    private class DownCastOperator<S> : Operator<T, S> where S : T {
      public DownCastOperator()
        : base(Markers.DownCastMarker) {
        Linq = (e) => linq.Expression.Convert(e, typeof(S));
        Apply = (o) => (S)o;
      }
    }
    public class ExplicitConvertOperator<S> : Operator<T, S> {
      private static System.Reflection.MethodInfo F;
      public static System.Reflection.MethodInfo GetF() {
        if (F == null) {
          foreach (var m in typeof(T).GetMethods()) {
            if (!(m.Name == "op_Explicit")) continue;
            if (m.GetParameters().Count() != 1) continue;
            if (!m.IsStatic) continue;
            if (m.ReturnType != typeof(S)) continue;
            if (m.GetParameters()[0].ParameterType != typeof(T)) continue;
            F = m;
            break;
          }
          if (F == null) throw new NotSupportedException();
        }
        return F;
      }
      public ExplicitConvertOperator()
        : base(Markers.DownCastMarker) {
        Linq = (e) => {
          return linq.Expression.Call(GetF(), e);
        };
        Inverse = (s) => {
          return new Expr<S>.ExplicitConvertOperator<T>().Make(s);
        };
      }
    }
  }

  public class OperatorMarker {
    public enum Container {
      Math, InstanceType, None, Shader, NoShader
    }
    public readonly string Name;
    public OperatorMarker(string Name) { this.Name = Name; }
    public Func<string, string> Unary { internal get; set; }
    public Func<string, string> ShaderUnary { internal get; set; }
    public Func<string, string, string> Binary { internal get; set; }
    public Func<string, string, string> ShaderBinary { internal get; set; }
    public Func<string, string, string, string> Trinary { internal get; set; }
    public Func<string, string, string, string> ShaderTrinary { internal get; set; }

    public Func<linq.Expression, linq.Expression> LinqUnary { internal get; set; }
    public Func<linq.Expression, linq.Expression, linq.Expression> LinqBinary { internal get; set; }
    public Func<linq.Expression, linq.Expression, linq.Expression, linq.Expression> LinqTrinary { internal get; set; }

    public Container Cont = Container.None;
    public bool Infix {
      set {
        if (value) {
          Unary = s => Name + s;
          Binary = (s, t) => s + " " + Name + " " + t;
        }
      }
    }
    public Func<string, string> Unary0 {
      get {
        if (Unary != null) return Unary;
        else if (Cont == Container.InstanceType) {
          return a => a + "." + Name + ")";
        }  else if (Cont == Container.Math) {
          return a => "Math." + Name + a + ")";
        }
        throw new NotSupportedException();
      }
    }


    public Func<string, string, string> Binary0 {
      get {
        if (Binary != null) return Binary;
        else if (Cont == Container.InstanceType) {
          return (a,b) => a + "." + Name + b + ")";
        } else if (Cont == Container.Math) {
          return (a,b) => "Math." + Name + a + ", " + b + ")";
        }
        throw new NotSupportedException();
      }
    }
    public Func<string, string, string, string> Trinary0 {
      get {
        if (Trinary != null) return Trinary;
        else if (Cont == Container.InstanceType) {
          return (a, b, c) => a + "." + Name + b + ", " + c + ")";
        } else if (Cont == Container.Math) {
          return (a, b, c) => "Math." + Name + a + ", " + b + ", " + c + ")";
        }
        throw new NotSupportedException();
      }
    }
  }
  public class BaseOperator {
    public bool DoConstantFold = true;
    public readonly OperatorMarker Marker;
    public BaseOperator(OperatorMarker Marker) { this.Marker = Marker; }
  }
  public abstract partial class TranslateEval<EVAL> : Eval<EVAL> where EVAL : Eval<EVAL> {
    public override Eval<EVAL>.Info<T> ArityX<T, ARITY>(ArityInfo<T, ARITY>.ArityExpr value) {
      Expr<double>[] Values = new Expr<double>[value.Values.Count];
      for (int i = 0; i < value.Values.Count; i++) {
        Values[i] = Translate(value.Values[i]);
      }
      return new MyInfo<T>() { Value = ArityInfo<T, ARITY>.ArityExpr.Make(Values) };
    }
    public override Eval<EVAL>.Info<V> Operate<S, T, U, V>(Operation<S, T, U, V> op) {
      return new MyInfo<V>() { Value = op.Op.Make(Translate(op.Value1), Translate(op.Value2), Translate(op.Value3)) };
    }
    public override Eval<EVAL>.Info<U> Operate<S, T, U>(Operation<S, T, U> op) {
      return new MyInfo<U>() { Value = op.Op.Make(Translate(op.Value1), Translate(op.Value2)) };
    }
    public override Eval<EVAL>.Info<T> Operate<S, T>(Operation<S, T> op) {
      return new MyInfo<T>() { Value = op.Op.Make(Translate(op.Value)) };
    }
  }
  public partial class ApplyEval<X> : Eval<ApplyEval<X>>, ForceEval, TableEval<ApplyEval<X>> {
    public override Info<T> Operate<S, T>(Operation<S, T> op) {
      var value1 = ((MyInfo<S>)op.Value.Eval(this)).Value;
      return new MyInfo<T>() { Value = x => op.Op.Apply(value1(x)) };
    }
    public override Info<U> Operate<S, T, U>(Operation<S, T, U> op) {
      var value1 = ((MyInfo<S>)op.Value1.Eval(this)).Value;
      var value2 = ((MyInfo<T>)op.Value2.Eval(this)).Value;
      return new MyInfo<U>() {
        Value = x => op.Op.Apply(value1(x),
                                 value2(x))
      };
    }
    public override Info<V> Operate<S, T, U, V>(Operation<S, T, U, V> op) {
      var value1 = ((MyInfo<S>)op.Value1.Eval(this)).Value;
      var value2 = ((MyInfo<T>)op.Value2.Eval(this)).Value;
      var value3 = ((MyInfo<U>)op.Value3.Eval(this)).Value;
      return new MyInfo<V>() {
        Value = x => {
          return op.Op.Apply(value1(x), value2(x), value3(x));
        }
      };
    }
    public override Info<T> ArityX<T, ARITY>(ArityInfo<T, ARITY>.ArityExpr value) {
      Func<X, double>[] dX = new Func<X, double>[value.Values.Count];
      for (int i = 0; i < value.Values.Count; i++) {
        dX[i] = ((MyInfo<double>)value.Values[i].Eval(this)).Value;
      }

      return new MyInfo<T>() { Value = x => {
        var d = new double[dX.Length];
        for (int i = 0; i < dX.Length; i++) {
          d[i] = dX[i](x);
        }
        var t = ArityInfo<T, ARITY>.Instance.ToT(d);
        return t;
      } };
    }
  }

  public abstract partial class Eval<EVAL> where EVAL : Eval<EVAL> {
    public abstract Info<T> Operate<S, T>(Operation<S, T> op);
    public abstract Info<U> Operate<S, T, U>(Operation<S, T, U> op);
    public abstract Info<V> Operate<S, T, U, V>(Operation<S, T, U, V> op);
    public abstract Info<T> ArityX<T, ARITY>(ArityInfo<T, ARITY>.ArityExpr value)
      where ARITY : ArityInfo<T, ARITY>, new();
  }
  public interface CoreOperation : BaseExpr {
    BaseOperator BaseOperator { get; }
  }
  public abstract class CoreOperation<T,SELF> : Expr<T>, CoreOperation where SELF : CoreOperation<T,SELF> {
    private readonly int HashCode;
    public CoreOperation(int HashCode) {
      this.HashCode = HashCode;
    }
    public abstract BaseOperator BaseOperator { get; }

    protected abstract bool Equals0(SELF other);
    public override int GetHashCode() {
       return HashCode;
    }
    public override bool Equals(object obj) {
      if (object.ReferenceEquals(this, obj)) return true;
      if (!(obj is SELF)) return false;
      var obj0 = (SELF)obj;
      if (HashCode != obj0.HashCode) return false;
      return Equals0(obj0);
    }
  }
  public interface UnaryOperation : CoreOperation {
    Type TypeOfS { get; }
    Expr BaseValue { get; }
  }

  public class Operation<S, T> : CoreOperation<T, Operation<S,T>>, UnaryOperation {
    public readonly Operator<S, T> Op; 
    public readonly Expr<S> Value;

    public override BaseOperator BaseOperator { get { return Op; } }
    public Expr BaseValue { get { return Value; } }

    public Operation(Operator<S, T> op, Expr<S> value) : base(op.GetHashCode() + value.GetHashCode()) {
      this.Op = op; this.Value = value;
    }
    public Type TypeOfS { get { return typeof(S); } }

    public override bool HasCurrentValue {
      get {
        return Value.HasCurrentValue;
      }
    }

    protected override bool Equals0(Operation<S, T> other) {
      return (Op == other.Op) && (Value.Equals(other.Value));
    }
    public override bool Solve(Assign P, Expr<T> other) {
      var ret = base.Solve(P, other);
      ret = ret || Op.XInverse == null ? ret : Value.Solve(P, Op.XInverse(Value, other));
      var expand = ret ? null : Op.Expand(Value);
      ret = expand == null ? ret : expand.Solve(P, other);
      return ret;
    }
    public override string ToString() {
      if (Op.Marker.Name.EndsWith("("))
        return Op.Marker.Name + Value + ")";
      //else if (Op.Marker is Markers.AccessMarker) {
      //  return Value + "[" + ((Markers.AccessMarker)Op.Marker).Idx + "]";
    //  } 
      else return Op.Marker.Name + Value;
    }
    public override Eval<EVAL>.Info<T> Eval<EVAL>(Eval<EVAL> txt) {
      var result = Op.Expand(Value);
      if (result != null) return result.Eval(txt);
      return txt.Operate(this);
    }
  }
  public partial class Operation<S, T, U> : CoreOperation<U,Operation<S,T,U>> {
    public readonly Operator<S, T, U> Op;
    public readonly Expr<S> Value1;
    public readonly Expr<T> Value2;
    public override BaseOperator BaseOperator { get { return Op; } }
    public Operation(Operator<S, T, U> op, Expr<S> value1, Expr<T> value2)
      : base(op.GetHashCode() + value1.GetHashCode() + value2.GetHashCode()) {
      this.Op = op; this.Value1 = value1; this.Value2 = value2;
    }
    protected override bool Equals0(Operation<S, T, U> other) {
      return Op == other.Op && Value1.Equals(other.Value1) && Value2.Equals(other.Value2);
    }
    public override bool HasCurrentValue {
      get {
        // exception, tex 2D.
        return Value1.HasCurrentValue && Value2.HasCurrentValue && Op.Marker != Markers.Tex2DMarker;
      }
    }

    public override bool Solve(Assign P, Expr<U> other) {
      var ret = base.Solve(P, other);
      ret = ret || Op.XInverse0 == null ? ret : Value1.Solve(P, Op.XInverse0(Value1, other, Value2));
      ret = ret || Op.XInverse1 == null ? ret : Value2.Solve(P, Op.XInverse1(Value2, other, Value1));
      var expand = ret ? null : Op.Expand(Value1, Value2);
      ret = expand == null ? ret : expand.Solve(P, other);
      return ret;
    }
    public override string ToString() {
      if (Op.Marker.Name.EndsWith("("))
        return Op.Marker.Name + Value1 + ", " + Value2 + ")";
      else return "(" + Value1.ToString() + " " + Op.Marker.Name + " " + Value2 + ")";
    }
    public override Eval<EVAL>.Info<U> Eval<EVAL>(Eval<EVAL> txt) {
      var expand = Op.Expand(Value1, Value2);
      if (expand != null) return expand.Eval(txt);
      else return txt.Operate(this);
    }
  }
  public class Operation<S, T, U, V> : CoreOperation<V,Operation<S,T,U,V>> {
    public readonly Operator<S, T, U, V> Op;
    public readonly Expr<S> Value1;
    public readonly Expr<T> Value2;
    public readonly Expr<U> Value3;
    public override BaseOperator BaseOperator { get { return Op; } }
    public Operation(Operator<S, T, U, V> op, Expr<S> value1, Expr<T> value2, Expr<U> value3) : 
    base(op.GetHashCode() + value1.GetHashCode() + value2.GetHashCode() + value3.GetHashCode()) {
      this.Op = op; this.Value1 = value1; this.Value2 = value2;
      this.Value3 = value3;
    }
    public override bool HasCurrentValue {
      get {
        return Value1.HasCurrentValue && Value2.HasCurrentValue && Value3.HasCurrentValue;
      }
    }
    protected override bool Equals0(Operation<S, T, U, V> other) {
      return Op == other.Op && Value1.Equals(other.Value1) && Value2.Equals(other.Value2) && Value3.Equals(other.Value3);
    }
    public override bool Solve(Assign P, Expr<V> other) {
      var ret = base.Solve(P, other);
      ret = ret || Op.XInverse0 == null ? ret : Value1.Solve(P, Op.XInverse0(Value1, other, Value2, Value3));
      ret = ret || Op.XInverse1 == null ? ret : Value2.Solve(P, Op.XInverse1(Value2, other, Value1, Value3));
      ret = ret || Op.XInverse2 == null ? ret : Value3.Solve(P, Op.XInverse2(Value3, other, Value1, Value2));
      var expand = ret ? null : Op.Expand(Value1, Value2, Value3);
      ret = expand == null ? ret : expand.Solve(P, other);
      return ret;
    }
    public override string ToString() {
      if (Op.Marker.Name.EndsWith("("))
        return Op.Marker.Name + Value1 + ", " + Value2 + ", " + Value3 + ")";
      var sp = Op.Marker.Name.Split(',');
      return Value1.ToString() + " " + sp[0] + " " + Value2 + " " + sp[1] + " " + Value3;
    }
    public override Eval<EVAL>.Info<V> Eval<EVAL>(Eval<EVAL> txt) {
      var expand = Op.Expand(Value1, Value2, Value3);
      if (expand != null) return expand.Eval(txt);
      return txt.Operate(this);
    }
  }
  public static class Operations {
    public static Expr<T> Make<S, T>(Operator<S, T> op, Expr<S> value1) {
      if (value1 is Constant<S> && op.DoConstantFold) {
        var expand = op.Fold((Constant<S>) value1);
        if (expand != null) return expand;
        //return new Constant<T>(op.Apply(((Constant<S>)value1).Value));
      } 
      return new Operation<S, T>(op, value1); 
    }
    public static Expr<U> Make<S, T, U>(Operator<S, T, U> op, Expr<S> value1, Expr<T> value2) {
      if (value1 is Constant<S> && value2 is Constant<T> && op.DoConstantFold) {
        var expand = op.Fold((Constant<S>)value1, (Constant<T>)value2);
        if (expand != null) return expand;
        //return new Constant<U>(op.Apply(((Constant<S>)value1).Value, ((Constant<T>)value2).Value));
      } 
      return new Operation<S, T, U>(op, value1, value2);
    }
    public static Expr<V> Make<S, T, U, V>(Operator<S, T, U, V> op, Expr<S> value1, Expr<T> value2, Expr<U> value3) {
      if (value1 is Constant<S> && value2 is Constant<T> && value3 is Constant<U> && op.DoConstantFold) {
        var expand = op.Fold((Constant<S>)value1, (Constant<T>)value2, (Constant<U>)value3);
        if (expand != null) return expand;
        //return new Constant<V>(op.Apply(((Constant<S>)value1).Value, ((Constant<T>)value2).Value, ((Constant<U>)value3).Value));
      } 
      return new Operation<S, T, U, V>(op, value1, value2, value3);
    }
    public static Expr<T> Make<T>(T t) { return new Constant<T>(t); }
  }
  public interface UnaryOperator {
    Func<string, string> BaseShader { get; }
  }
  public class Operator<S, T> : BaseOperator, UnaryOperator {
    public Operator(OperatorMarker Marker)
      : base(Marker) {
      AsText = (a) => Marker.Unary0(a);
      Linq = e => DefaultLinq(this, e);
      Shader = e => DefaultShader(this, e);
      Expand = e => null;
      Fold = (e0) => {
        var ret = Expand(e0);
        if (ret != null) return ret;
        if (Apply != null) return new Constant<T>(Apply(e0.Value));
        return null;
      };
    }
    public virtual Func<S, T> Apply { get; set; }
    public virtual Func<Expr<S>, Expr<T>> Expand { get; set; }
    public Func<Constant<S>, Expr<T>> Fold { get; set; }
    public Func<Expr<T>, Expr<S>> Inverse {
      set {
        XInverse = (s, t) => value(t);
      }
    }
    public virtual Func<Expr<S>, Expr<T>, Expr<S>> XInverse { get; set; }
    public Func<Expr<S>, Expr<T>> Make {
      get { return (value1) => Operations.Make(this, value1); }
    }
    public Func<linq.Expression, linq.Expression> Linq;
    private static linq.Expression DefaultLinq(Operator<S, T> op, linq.Expression e) {
      if (op.Marker.LinqUnary != null) return op.Marker.LinqUnary(e);
      else if (op.Marker.Cont == OperatorMarker.Container.InstanceType) {
        if (op.Marker.Name.EndsWith("(")) { // member method.
          return linq.Expression.Call(e, op.Marker.Name.Substring(0, op.Marker.Name.Length - 1), new Type[] { }, new linq.Expression[] { });
        } else { // property access
          return linq.Expression.Property(e, op.Marker.Name);
        }
      } else if (op.Marker.Cont == OperatorMarker.Container.Math) {
        return linq.Expression.Call(typeof(Math), op.Marker.Name.Substring(0, op.Marker.Name.Length - 1), new Type[] { }, new linq.Expression[] { e });
      }
      throw new NotSupportedException();
    }
    public Func<string, string> Shader;
    public Func<string, string> BaseShader { get { return Shader; } }
    private static string DefaultShader(Operator<S, T> op, string e1) {
      if (op.Marker.ShaderUnary != null) return op.Marker.ShaderUnary(e1);
      //else if (op.Marker.Cont != OperatorMarker.Container.None) {
        if (op.Marker.Unary != null) return op.Marker.Unary(e1);
        else if (op.Marker.Name.EndsWith("(")) return op.Marker.Name.ToLower() + e1 + ")";
      //}
      throw new NotSupportedException();
    }
    public Func<string, string> AsText {
      get;
      set;
    }
  }
  public class Operator<S, T, U> : BaseOperator {
    public Operator(OperatorMarker Marker) : base(Marker) {
      AsText = (a, b) => Marker.Binary0(a, b);
      Linq = (e0, e1) => DefaultLinq(this, e0, e1);
      Shader = (e0,e1) => DefaultShader(this, e0, e1);
      Expand = (e0,e1) => null;
      Fold = (e0, e1) => {
        var ret = Expand(e0, e1);
        if (ret != null) return ret;
        if (Apply != null) return new Constant<U>(Apply(e0.Value, e1.Value));
        return null;
      };
    }
    public virtual Func<S, T, U> Apply { get; set; }
    public virtual Func<Expr<S>, Expr<T>, Expr<U>> Expand { get; set; }
    public Func<Constant<S>, Constant<T>, Expr<U>> Fold { get; set; }

    public Func<string, string, string> AsText {
      get;
      set;
    }

    public Func<Expr<U>, Expr<T>, Expr<S>> Inverse0 {
      set {
        XInverse0 = (s, u, t) => value(u, t);
      }
    }
    public Func<Expr<U>, Expr<S>, Expr<T>> Inverse1 {
      set {
        XInverse1 = (t, u, s) => value(u, s);
      }
    }

    public virtual Func<Expr<S>, Expr<U>, Expr<T>, Expr<S>> XInverse0 { get; set; }
    public virtual Func<Expr<T>, Expr<U>, Expr<S>, Expr<T>> XInverse1 { get; set; }


    public Func<Expr<S>, Expr<T>, Expr<U>> Make {
      get { return (value1, value2) => Operations.Make(this, value1, value2); }
    }
    public Func<linq.Expression, linq.Expression, linq.Expression> Linq;
    private static linq.Expression DefaultLinq(Operator<S, T, U> op, linq.Expression e0, linq.Expression e1) {
      if (op.Marker.LinqBinary != null) return op.Marker.LinqBinary(e0, e1);
      else if (op.Marker.Cont == OperatorMarker.Container.InstanceType) {
        if (op.Marker.Name.EndsWith("(")) { // member method.
          return linq.Expression.Call(e0, op.Marker.Name.Substring(0, op.Marker.Name.Length - 1), new Type[] { }, new linq.Expression[] { e1 });
        } else throw new NotSupportedException();
      } else if (op.Marker.Cont == OperatorMarker.Container.Math) {
        return linq.Expression.Call(typeof(Math), op.Marker.Name.Substring(0, op.Marker.Name.Length - 1), new Type[] { }, new linq.Expression[] { e0, e1 });
      }
      throw new NotSupportedException();
    }
    public Func<string, string,string> Shader;
    private static string DefaultShader(Operator<S, T, U> op, string e1, string e2) {
      if (op.Marker.ShaderBinary != null) return op.Marker.ShaderBinary(e1, e2);
      //else if (op.Marker.Cont != OperatorMarker.Container.None) {
        if (op.Marker.Binary != null) return op.Marker.Binary(e1, e2);
        else if (op.Marker.Name.EndsWith("(")) return op.Marker.Name.ToLower() + e1 + ", " + e2 + ")";
      //}
      throw new NotSupportedException();
    }
  }
  public class Operator<S, T, U, V> : BaseOperator {
    public Operator(OperatorMarker Marker) : base(Marker) {
      Shader = (e0, e1, e2) => DefaultShader(this, e0, e1, e2);
      Linq = (e0, e1, e2) => DefaultLinq(this, e0, e1, e2);
      Expand = (e0, e1, e2) => null;
      Fold = (e0, e1, e2) => {
        var ret = Expand(e0, e1, e2);
        if (ret != null) return ret;
        if (Apply != null) return new Constant<V>(Apply(e0.Value, e1.Value, e2.Value));
        return null;
      };
    }
    public virtual Func<S, T, U, V> Apply { get; set; }
    public virtual Func<Expr<S>, Expr<T>, Expr<U>, Expr<V>> Expand { get; set; }
    public Func<Constant<S>, Constant<T>, Constant<U>, Expr<V>> Fold { get; set; }
    public virtual Func<Expr<V>, Expr<T>, Expr<U>, Expr<S>> Inverse0 {
      set {
        XInverse0 = (s, v, t, u) => value(v, t, u);
      }
    }
    public virtual Func<Expr<V>, Expr<S>, Expr<U>, Expr<T>> Inverse1 { set {
        XInverse1 = (t, v, s, u) => value(v, s, u);
    } }
    public virtual Func<Expr<V>, Expr<S>, Expr<T>, Expr<U>> Inverse2 { set {
        XInverse2 = (u, v, s, t) => value(v, s, t);
    } }

    public virtual Func<Expr<S>, Expr<V>, Expr<T>, Expr<U>, Expr<S>> XInverse0 { get; set; }
    public virtual Func<Expr<T>, Expr<V>, Expr<S>, Expr<U>, Expr<T>> XInverse1 { get; set; }
    public virtual Func<Expr<U>, Expr<V>, Expr<S>, Expr<T>, Expr<U>> XInverse2 { get; set; }

    public Func<Expr<S>, Expr<T>, Expr<U>, Expr<V>> Make {
      get { return (value1, value2, value3) => Operations.Make(this, value1, value2, value3); }
    }
    public Func<linq.Expression, linq.Expression, linq.Expression, linq.Expression> Linq;
    private static linq.Expression DefaultLinq(Operator<S, T, U, V> op, linq.Expression e0, linq.Expression e1, linq.Expression e2) {
      if (op.Marker.LinqTrinary != null) return op.Marker.LinqTrinary(e0, e1, e2);
      else if (op.Marker.Cont == OperatorMarker.Container.InstanceType) {
        if (op.Marker.Name.EndsWith("(")) { // member method.
          return linq.Expression.Call(e0, op.Marker.Name.Substring(0, op.Marker.Name.Length - 1), new Type[] { }, new linq.Expression[] { e1, e2 });
        } else throw new NotSupportedException();
      } else if (op.Marker.Cont == OperatorMarker.Container.Math) {
        return linq.Expression.Call(typeof(Math), op.Marker.Name.Substring(0, op.Marker.Name.Length - 1), new Type[] { }, new linq.Expression[] { e0, e1, e2 });
      }
      throw new NotSupportedException();
    }
    public Func<string, string, string, string> Shader;
    private static string DefaultShader(Operator<S, T, U, V> op, string e1, string e2, string e3) {
      if (op.Marker.ShaderTrinary != null) return op.Marker.ShaderTrinary(e1, e2, e3);
      //else if (op.Marker.Cont != OperatorMarker.Container.None) {
        if (op.Marker.Trinary != null) return op.Marker.Trinary(e1, e2, e3);
        else if (op.Marker.Name.EndsWith("(")) return op.Marker.Name.ToLower() + e1 + ", " + e2 + ", " + e3 + ")";
      //}
      throw new NotSupportedException();
    }
  }
  public class AssociativeOperator<S> : Operator<S, S> {
    public AssociativeOperator(OperatorMarker Marker) : base(Marker) { }
    public override Func<Expr<S>, Expr<S>, Expr<S>> XInverse {
      get { return (x,s) => Operations.Make(this, s); }
      set { throw new NotSupportedException(); }
    }
  }
  public class AssociativeOperator<S, T> : Operator<S, S, T> {
    public AssociativeOperator(OperatorMarker Marker) : base(Marker) { }
    public override Func<Expr<S>, Expr<T>, Expr<S>, Expr<S>> XInverse1 {
      get { 
        return XInverse0; 
      }
      set { throw new NotSupportedException(); }
    }
  }
  public static partial class Markers {
    public static readonly OperatorMarker PlusMarker = new OperatorMarker("+") { 
      Infix = true,
      LinqUnary = linq.Expression.UnaryPlus,
      LinqBinary = linq.Expression.Add,
    };
    public static readonly OperatorMarker MinusMarker = new OperatorMarker("-") { 
      Infix = true,
      ShaderUnary = (a) => "-" + a,
      LinqUnary = linq.Expression.Negate,
      LinqBinary = linq.Expression.Subtract,
    };
    public static readonly OperatorMarker ClampMarker = new OperatorMarker("Clamp(") {
      Trinary = (a, b, c) => a + ".Clamp(" + b + ", " + c + ")",
      ShaderTrinary = (a, b, c) => "clamp(" + a + ", " + b + ", " + c + ")",
      LinqTrinary = (a,b,c) => linq.Expression.Call(a, "Clamp", new Type[] {}, new linq.Expression[] { b, c }),
    };
    public static readonly OperatorMarker RestrictMarker = new OperatorMarker("Restrict(") { }; 
    public static readonly OperatorMarker MultiplyMarker = new OperatorMarker("*") { 
      Infix = true,
      LinqBinary = linq.Expression.Multiply, 
    };
    public static readonly OperatorMarker DivideMarker = new OperatorMarker("/") { 
      Infix = true,
      LinqBinary = linq.Expression.Divide,  
    };
    public static readonly OperatorMarker ModMarker = new OperatorMarker("%") { 
      Infix = true,
      LinqBinary = linq.Expression.Modulo,
    };
    public static readonly OperatorMarker FracMarker = new OperatorMarker("Frac") {
      ShaderUnary = (a) => "frac(" + a + ")",
      LinqUnary = e => 
//       linq.Expression.Call(typeof(Math), "Abs", new Type[] {}, e),

        AbsMarker.LinqUnary(ModMarker.LinqBinary(e, linq.Expression.Constant(1d))),
    };
    public static readonly OperatorMarker LengthMarker = new OperatorMarker("Length(") {
      //Unary = p => "((Vector) " + p + ").Length",
      ShaderUnary = p => "length(" + p + ")",
      //LinqUnary = e => linq.Expression.Property(linq.Expression.ConvertChecked(e, typeof(Vector)), "Length"),
    };
    public static readonly OperatorMarker SinCosMarker = new OperatorMarker("SinCos(") {
    };

    public static readonly OperatorMarker SinMarker = new OperatorMarker("Sin(") { 
      Cont = OperatorMarker.Container.Math,
    };
    public static readonly OperatorMarker CosMarker = new OperatorMarker("Cos(") {
      Cont = OperatorMarker.Container.Math,
    };

    public static readonly OperatorMarker TanMarker = new OperatorMarker("Tan(") {
      Cont = OperatorMarker.Container.Math,
    };

    public static readonly OperatorMarker ATanMarker = new OperatorMarker("Atan(") { Cont = OperatorMarker.Container.Math };
    public static readonly OperatorMarker ATan2Marker = new OperatorMarker("Atan2(") { Cont = OperatorMarker.Container.Math };
    public static readonly OperatorMarker LerpMarker = new OperatorMarker("Lerp(") {
      Trinary = (a, b, c) => b + ".Lerp(" + c + ", " + a + ")",
      ShaderTrinary = (a, b, c) => "lerp(" + b + ", " + c + ", " + a + ")",
    };

    public static readonly OperatorMarker EqualMarker = new OperatorMarker("==") { 
      Infix = true, 
      LinqBinary = linq.Expression.Equal, 
    };
    public static readonly OperatorMarker NotEqualMarker = new OperatorMarker("!=") {
      Infix = true,
      LinqBinary = linq.Expression.NotEqual,
    };
    public static readonly OperatorMarker GreaterEqualMarker = new OperatorMarker(">=") { 
      Infix = true,
      LinqBinary = linq.Expression.GreaterThanOrEqual,
    };
    public static readonly OperatorMarker LessEqualMarker = new OperatorMarker("<=") { 
      Infix = true,
      LinqBinary = linq.Expression.LessThanOrEqual,
    };
    public static readonly OperatorMarker GreaterMarker = new OperatorMarker(">") { 
      Infix = true,
      LinqBinary = linq.Expression.GreaterThan,
    };
    public static readonly OperatorMarker LessMarker = new OperatorMarker("<") { 
      Infix = true,
      LinqBinary = linq.Expression.LessThan,
    };
    public static readonly OperatorMarker ConditionMarker = new OperatorMarker("?,:") { 
      ShaderTrinary = (a,b,c) => a + " ? " + b + " : " + c,
      Trinary = (a, b, c) => a + " ? " + b + " : " + c,
      LinqTrinary = linq.Expression.Condition,
    };
    public static readonly OperatorMarker GuardMarker = new OperatorMarker("Guard") {  };
    public static readonly OperatorMarker RelaxMarker = new OperatorMarker("Relax") {  };

    public static readonly OperatorMarker DoubleToInt = new OperatorMarker("DoubleToInt(") {
      Unary = (a) => "((int)" + a + ")",
      LinqUnary = a => linq.Expression.Convert(a, typeof(int)),
      ShaderUnary = (a) => a,
    };
    public static readonly OperatorMarker IntToDouble = new OperatorMarker("IntToDouble(") {
      Unary = (a) => "((double)" + a + ")",
      LinqUnary = a => linq.Expression.Convert(a, typeof(double)),
      ShaderUnary = (a) => a,
    };
    public static readonly OperatorMarker AndMarker = new OperatorMarker("&&") { 
      Infix = true, 
      ShaderBinary = (a,b) => a + " && " + b, 
      LinqBinary = linq.Expression.And,
    };
    public static readonly OperatorMarker OrMarker = new OperatorMarker("||") { 
      Infix = true,
      ShaderBinary = (a, b) => a + " || " + b,
      LinqBinary = linq.Expression.Or,
    };
    public static readonly OperatorMarker Tex2DMarker = new OperatorMarker("tex2D(") { 
      Cont = OperatorMarker.Container.Shader,
      ShaderBinary = (a,b) => "tex2D(" + a + ", " + b + ")",
    };
    public static readonly OperatorMarker NotMarker = new OperatorMarker("!") { 
      Infix = true, ShaderUnary = (a) => "!" + a, 
      LinqUnary = linq.Expression.Not,
    };
    public static readonly OperatorMarker SqrtMarker = new OperatorMarker("Sqrt(") { Cont = OperatorMarker.Container.Math };
    public static readonly OperatorMarker RSqrtMarker = new OperatorMarker("RSqrt(") { Cont = OperatorMarker.Container.Shader, Unary = s => "1d / " + s + " * " + s };
    public static readonly OperatorMarker SquareMarker = new OperatorMarker("Square(") { Cont = OperatorMarker.Container.Shader, 
      ShaderUnary = s => s + " * " + s,
      Unary = s => s + " * " + s,
      LinqUnary = s => linq.Expression.Multiply(s, s),
    };
    public static readonly OperatorMarker PowMarker = new OperatorMarker("Pow(") { Cont = OperatorMarker.Container.Math };
    public static readonly OperatorMarker IsNanMarker = new OperatorMarker("IsNaN(") { 
      Cont = OperatorMarker.Container.None,
      Unary = s => "double.IsNan(" + s + ")",
      ShaderUnary = s => "isnan(" + s + ")",
      LinqUnary = e => linq.Expression.Call(typeof(double), "IsNaN", new Type[] {},  e),
    };
    public static readonly OperatorMarker IsInfMarker = new OperatorMarker("IsInfinity(") {
      Cont = OperatorMarker.Container.None,
      Unary = s => "double.IsInfinity(" + s + ")",
      ShaderUnary = s => "isinf(" + s + ")",
      LinqUnary = e => linq.Expression.Call(typeof(double), "IsInfinity", new Type[] { }, e),
    };
    public static readonly OperatorMarker SignMarker = new OperatorMarker("Sign(") { Cont = OperatorMarker.Container.Math };
    public static readonly OperatorMarker AbsMarker = new OperatorMarker("Abs(") { 
      Cont = OperatorMarker.Container.Math, 
      LinqUnary = e => linq.Expression.Call(typeof(Math), "Abs", new Type[] { }, e),
    };
    public static readonly OperatorMarker LogMarker = new OperatorMarker("Log(") { Cont = OperatorMarker.Container.Math };
    public static readonly OperatorMarker Log10Marker = new OperatorMarker("Log10(") { Cont = OperatorMarker.Container.Math };
    public static readonly OperatorMarker ExpMarker = new OperatorMarker("Exp(") { Cont = OperatorMarker.Container.Math };
    public static readonly OperatorMarker Exp2Marker = new OperatorMarker("Exp2(") { Cont = OperatorMarker.Container.Shader };
    public static readonly OperatorMarker MinMarker = new OperatorMarker("Min(") { Cont = OperatorMarker.Container.Math };
    public static readonly OperatorMarker MaxMarker = new OperatorMarker("Max(") { Cont = OperatorMarker.Container.Math };

    public static readonly OperatorMarker RoundMarker = new OperatorMarker("Round(") { Cont = OperatorMarker.Container.Math };
    public static readonly OperatorMarker FloorMarker = new OperatorMarker("Floor(") { Cont = OperatorMarker.Container.Math };
    public static readonly OperatorMarker CeilingMarker = new OperatorMarker("Ceiling(") { Cont = OperatorMarker.Container.Math };
    public static readonly OperatorMarker ToStringMarker = new OperatorMarker("ToString(") { Cont = OperatorMarker.Container.InstanceType };

    public class UnaryAccessOperator : OperatorMarker {
      public UnaryAccessOperator(string Name) : base(Name) {
        Unary = a => a + "." + this.Name;
        ShaderUnary = a => a + "." + this.Name.ToLower();
        LinqUnary = e => linq.Expression.Property(e, Name);
      }
    }
    public static readonly OperatorMarker DownCastMarker = new OperatorMarker("DownCast") {};
    public static readonly OperatorMarker CheckNanMarker = new OperatorMarker("CheckNan") {
      Cont = OperatorMarker.Container.None,
    };
  }
}
